package com.means.polymerize.assembly.aspect.logs;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.extra.servlet.ServletUtil;
import com.alibaba.fastjson.JSON;
import com.means.polymerize.common.constants.Global;
import com.means.polymerize.common.lang.DataResult;
import com.means.polymerize.common.util.AddressUtil;
import com.means.polymerize.common.util.ParameterUtil;
import com.means.polymerize.common.util.ServletUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 全局日志存储工具
 *
 * @author xuhaifneg
 * @date 2021.03.31
 */
@Slf4j
@Component
@SuppressWarnings({"AlibabaUndefineMagicConstant", "unused"})
public class LogsDomain {
    private static final String USER_AGENT = "User-Agent";
    private static final String API_LOG_OBJ_SAVE_KEY = "API_LOG_OBJ_SAVE_KEY";

    /**
     * Start request.
     */
    public static void startRequest() {
        if (Objects.isNull(RequestContextHolder.getRequestAttributes())) {
            return;
        }
        final HttpServletRequest request = ServletUtils.getRequest();
        final String parameterByJson = ParameterUtil.getParameterByJson(request);
        final String parameterByFormData = ParameterUtil.getParameterByFormData(request).toJSONString();
        final SysLog logEntity = SysLog.builder()
                .reqType(Global.NUM_I_2)
                .reqMethod(request.getMethod())
                .reqPath(request.getRequestURI())
                .reqStartTime(LocalDateTime.now())
                .reqToken(StpUtil.getTokenValue())
                .reqIp(ServletUtil.getClientIP(request))
                .userAgent(request.getHeader(USER_AGENT))
                .reqHeader(JSON.toJSONString(ParameterUtil.getHeaderMap(request)))
                .reqLocation(AddressUtil.getRealAddressByIP(ServletUtil.getClientIP(request)))
                .reqBody(request.getMethod().equalsIgnoreCase(Global.GET) ? parameterByFormData : parameterByJson)
                .build();
        if (StpUtil.isLogin()) {
            logEntity.setReqUserId(StpUtil.getLoginIdAsString());
        }
        request.setAttribute(API_LOG_OBJ_SAVE_KEY, logEntity);
        log.debug(String.format("IP: %s\tr-> %s\tp-> %s", logEntity.getReqIp(), logEntity.getReqApiId(), logEntity.getReqBody()));
    }

    /**
     * End request.
     *
     * @param output the output
     */
    public static void endRequest(DataResult<?> output) {
        if (Objects.isNull(output) || Objects.isNull(RequestContextHolder.getRequestAttributes())) {
            return;
        }
        final HttpServletRequest request = ServletUtils.getRequest();
        final AtomicReference<SysLog> logEntity =
                new AtomicReference<>((SysLog) request.getAttribute(API_LOG_OBJ_SAVE_KEY));
        if (Objects.isNull(logEntity.get())) {
            LogsDomain.startRequest();
            logEntity.set((SysLog) request.getAttribute(API_LOG_OBJ_SAVE_KEY));
        }
        logEntity.get()
                .setResMsg(output.getDesc())
                .setReqEndTime(LocalDateTime.now())
                .setResBody(JSON.toJSONString(output))
                .setResCode(String.valueOf(output.getCode()))
                .setReqCostTime(logEntity.get().getReqEndTime().toEpochSecond(ZoneOffset.of("+8")) - logEntity.get().getReqStartTime().toEpochSecond(ZoneOffset.of("+8")));
        try {
            logEntity.get().setReqUserId(StpUtil.getLoginIdAsString());
        } catch (Exception ignored) {
        }
        final int reqBodyLength = logEntity.get().getReqBody().length();
        if (reqBodyLength > 50000) {
            logEntity.get().setReqBody(String.format("{\"msg\": \"数据过长，无法写入 (length=%d)\"}", reqBodyLength));
        }
        log.info(String.format("本次请求耗时：%ss, 返回：%s", (logEntity.get().getReqCostTime() + 0.0) / 1000, logEntity.get().getReqBody()));
    }

}
